/*
 * sockets.c
 * 基于lwip修改
 *
 *  Created on: Jan 5, 2022
 *      Author: soliber
 */
//Include
#include "wifi.h"

#include <string.h>
#include <errno.h>

#include "inet.h"
#include "ip_addr.h"
#include "sockets.h"

#include "cmsis_os2.h"


//Define
#define LOG_TAG "sockets"

#define DEFAULT_CONN_TIMEOUT	5000
#define DEFAULT_RXBUF_SIZE		1000
#define DEFAULT_TXBUF_SIZE		1000
#define DEFAULT_RX_TIMEOUT		100
#define DEFAULT_TX_TIMEOUT		100

//Macro
#ifdef ERRNO
#define set_errno(err) errno = (err)
#else
#define set_errno(err)
#endif


//Typedef


//Variables
//socket列表以及锁
static wifi_socket_t sockets[NUM_SOCKETS]={};
static osMutexId_t socklock = NULL;


//Function Prototypes
static void sockets_lock(void);
static void sockets_unlock(void);


/**
 * @brief 获取指定socket描述符对应的socket对象
 *
 * @param s socket描述符
 *
 * @return struct wifi_socket* socket对象，若失败则返回NULL
 */
struct wifi_socket* get_socket(int s)
{
	return ((s < 0) || (s > NUM_SOCKETS))?NULL:&sockets[s];
}

/**
 * @brief sockets加锁
 */
static void sockets_lock(void)
{
	osStatus_t st;
	if(socklock == NULL)
	{
		socklock = osMutexNew(NULL);
	}

	st = osMutexAcquire(socklock, 5000);
	if(st != osOK)
	{
		log_e("sockets lock error:%d",st);
		Error_Handler();
	}
}

/**
 * @brief sockets解锁
 */
static void sockets_unlock(void)
{
	osStatus_t st;
	st = osMutexRelease(socklock);
	if(st != osOK)
	{
		log_e("sockets unlock error:%d",st);
		Error_Handler();
	}
}

/**
 * @brief 创建socket对象
 *
 * @param domain	域，此参数仅用于兼容BSD Socket而不会起到实际作用，
 * 					在BSD Socket中这个参数用于标识socket地址格式，对于IPv4地址应使用 AF_INET
 * @param type 		socket类型，此参数仅用于兼容BSD Socket而不会起到实际作用，
 * 					在BSD Socket中这个参数通常在创建流socket时会被指定为 SOCK_STREAM
 * @param protocol	协议类型，此参数仅用于兼容BSD Socket而不会起到实际作用，一般设为0
 *
 * @return 返回文件描述符或错误
 * @retval >=0 文件描述符
 * @retval -1 错误
 */
int wifi_socket(int domain, int type, int protocol)
{
	int fd = -1;

	UNUSED(domain);
	UNUSED(type);
	UNUSED(protocol);

	//wifi加锁
	WIFI_Lock();

	//查询连接状态
	int8_t link[4];
	AT_Enter();
	AT_Execute("AT+CIPSTATUS\r\n");
	assert(AT_Parser_CIPSTATUS(link)==0);
	AT_Exit();

	//wifi解锁
	WIFI_Unlock();


	//sockets加锁
	sockets_lock();

	//分配一个未使用的socket
	for(uint8_t i = 0;i<4;i++)
	{
		if((link[i]==-1)&&(sockets[i].lock == NULL))
		{
			fd = i;
			sockets[i].fd = fd;
			sockets[i].lock = osMutexNew(NULL);
			//默认选项
			sockets[i].connTimeout = DEFAULT_CONN_TIMEOUT;
			sockets[i].rxBufSize = DEFAULT_RXBUF_SIZE;
			sockets[i].txBufSize = DEFAULT_TXBUF_SIZE;
			sockets[i].rxTimeout = DEFAULT_RX_TIMEOUT;
			sockets[i].txTimeout = DEFAULT_TX_TIMEOUT;
			log_i("socket %d create",fd);
			break;
		}
	}

	//sockets解锁
	sockets_unlock();

	return fd;
}

/**
 * @brief 建立连接
 *
 * @param s socket描述符
 * @param name 远程主机
 * @param namelen name的长度
 *
 * @return 连接状态
 * @retval 0 连接成功
 * @retval -1 连接失败
 */
int wifi_connect(int s, struct sockaddr *name, socklen_t namelen)
{
	assert((s>=0)&&(s<NUM_SOCKETS));
	assert(name!=NULL);
	assert(socklock!=NULL);

	if(((struct sockaddr_in*)name)->sin_family == AF_INET)
	{
		//sockets加锁
		sockets_lock();
		//保存远程主机
		sockets[s].remote = *((struct sockaddr_in*)name);
		//分配buffer
		sockets[s].rxBuf = xStreamBufferCreate(sockets[s].rxBufSize,sockets[s].rxBufSize);
		sockets[s].txBuf = xStreamBufferCreate(sockets[s].txBufSize,sockets[s].txBufSize);
		assert(sockets[s].rxBuf!=NULL);
		assert(sockets[s].txBuf!=NULL);
		//sockets解锁
		sockets_unlock();
	}
	else
	{
		log_e("unsupported sin_family");
		return -1;
	}

	//wifi加锁
	WIFI_Lock();
	//设置远程主机
	char atbuf[64];
	snprintf(atbuf,sizeof(atbuf),"AT+CIPREMOTE=%d,%d,%s\r\n",s,
			ntohs(sockets[s].remote.sin_port),
			inet_ntoa(sockets[s].remote.sin_addr));
	AT_Enter();
	AT_Execute(atbuf);
	assert(AT_Parser_OK()==0);
	//查询连接状态
	log_i("wait socket %d connect...",s);
	uint32_t timeout = osKernelGetTickCount()+sockets[s].connTimeout;
	while(1)
	{
		int8_t link[4];
		AT_Execute("AT+CIPSTATUS\r\n");
		assert(AT_Parser_CIPSTATUS(link)==0);
		//已连接
		if(link[s]==1)
		{
			log_i("socket %d connected",s);
			break;
		}
		//未连接，等待
		else if(link[s]==-1)
		{
			if(osKernelGetTickCount()<timeout)
			{
				continue;
			}
			else
			{
				log_e("socket %d CIPSTATUS timeout(%dms,status:%d)",s,sockets[s].connTimeout,link[s]);
				AT_Exit();
				WIFI_Unlock();
				return -1;
			}
		}
		else
		{
			log_e("socket %d CIPSTATUS error",link[s]);
		}
	}
	AT_Exit();
	//wifi解锁
	WIFI_Unlock();

	return 0;
}

/**
 * @brief 连接终止
 *
 * @param s 要终止的socket文件描述符
 *
 * @return 操作状态
 * @retval 0 成功
 * @retval -1 失败
 */
int wifi_close(int s)
{
	assert((s>=0)&&(s<NUM_SOCKETS));

	sockets_lock();
	WIFI_Lock();

	//WIFI模块设置
	char atbuf[64];
	snprintf(atbuf,sizeof(atbuf),
			"AT+CIPREMOTE=%d,0,255.255.255.255\r\n",s);
	AT_Enter();
	AT_Execute(atbuf);
	assert(AT_Parser_OK()==0);

	//释放sockets资源
	vStreamBufferDelete(sockets[s].rxBuf);
	vStreamBufferDelete(sockets[s].txBuf);
	assert(osMutexDelete(sockets[s].lock)==osOK);
	memset(&sockets[s],0,sizeof(wifi_socket_t));

	WIFI_Unlock();
	sockets_unlock();

	return 0;
}

/**
 * @brief 接收
 *
 * @param s socket文件描述符
 * @param mem 用户接收缓冲区
 * @param len 要接受的数据长度
 * @param flags 标志位，用来修改I/O行为
 * 				MSG_DONTWAIT表示非阻塞执行，如果没有数据可用，函数将立刻返回
 *
 * @return 接收到的数据长度、EOF或错误
 * @retval >=0 接收到的数据长度
 * @retval 0 EOF
 * @retval -1 错误
 */
int wifi_recv(int s, void *mem, int len, unsigned int flags)
{
	assert((s>=0)&&(s<NUM_SOCKETS));
	assert(mem!=NULL);

	if(sockets[s].lock==NULL)
	{
		log_e("socket %d no valid",s);
		return -1;
	}

	if(len == 0)
		return 0;

	sockets_lock();
	size_t recved = xStreamBufferReceive(sockets[s].rxBuf,
			mem, len,
			(flags&MSG_DONTWAIT)?0:sockets[s].rxTimeout);
	sockets_unlock();

	return recved;
}

/**
 * @brief 发送
 *
 * @note 如果数据量较大，将分为多个数据包发送
 *
 * @param s socket文件描述符
 * @param dataptr 用户发送数据
 * @param size 用户发送数据量
 * @param flags 标志位，用来修改I/O行为
 * 				MSG_DONTWAIT表示非阻塞执行，如果没有数据可用，函数将立刻返回
 *
 * @return 发送的数据长度或错误
 * @retval >=0 发送的数据长度
 * @retval -1 错误
 */
int wifi_send(int s, void *dataptr, int size, unsigned int flags)
{
	assert((s>=0)&&(s<NUM_SOCKETS));
	assert(dataptr!=NULL);

	uint32_t tick = osKernelGetSysTimerCount();
	int sent=0;

	if(sockets[s].lock==NULL)
	{
		log_e("socket %d no valid",s);
		return -1;
	}

	if(size == 0)
		return 0;

	//数据打包发送
	while(size)
	{
		sent += packager_send(s,((uint8_t*)dataptr)+sent,size-sent,50);
		if(osKernelGetSysTimerCount() - tick >=
				((flags&MSG_DONTWAIT)?0:sockets[s].txTimeout))
		{
			break;
		}
	}
	return sent;
}

/**
 * @brief 获取本地socket地址
 *
 * @param s socket文件描述符
 * @param name 指向返回的socket地址
 * @param namelen name指向结构体的大小
 *
 * @return 操作状态
 * @retval 0 成功
 * @retval -1 错误
 */
int wifi_getsockname (int s, struct sockaddr *name, socklen_t *namelen)
{
	//TODO 未实现
	return -1;
}

/**
 * @brief 获取对端socket地址
 *
 * @param s socket文件描述符
 * @param name 指向返回的socket地址
 * @param namelen name指向结构体的大小
 *
 * @return 操作状态
 * @retval 0 成功
 * @retval -1 错误
 */
int wifi_getpeername (int s, struct sockaddr *name, socklen_t *namelen)
{
	//TODO 未实现
	return -1;
}

/**
 * @brief 获取socket选项
 *
 * @param s 		socket文件描述符
 * @param level 	指定socket选项所适用的协议，此参数仅用于兼容BSD Socket而不会起到实际作用，一般设为SOL_SOCKET
 * @param optname 	希望获取的选项，支持以下选项：
 * 					SO_RCVTIMEO 接收超时时间	(uint32_t)
 * 					SO_SNDTIMEO	发送超时时间	(uint32_t)
 * 					SO_RCBBUF	接收缓冲区大小	(uint16_t)
 * 					SO_SNDBUF	发送缓冲区大小	(uint16_t)
 * @param optval 	指向缓冲区的指针
 * @param optlen 	optval所指缓冲区空间大小
 *
 * @return 操作状态
 * @retval 0 成功
 * @retval -1 失败
 */
int wifi_getsockopt (int s, int level, int optname, void *optval, socklen_t *optlen)
{
	//TODO 未实现
	return -1;
}

/**
 * @brief 设置socket选项
 *
 * @param s 		socket文件描述符
 * @param level 	指定socket选项所适用的协议，此参数仅用于兼容BSD Socket而不会起到实际作用，一般设为SOL_SOCKET
 * @param optname 	希望设置的选项，支持以下选项：
 * 					SO_RCVTIMEO 接收超时时间	(uint32_t)
 * 					SO_SNDTIMEO	发送超时时间	(uint32_t)
 * 					SO_RCBBUF	接收缓冲区大小	(uint16_t)
 * 					SO_SNDBUF	发送缓冲区大小	(uint16_t)
 * @param optval 	指向缓冲区的指针
 * @param optlen 	optval所指缓冲区空间大小
 *
 * @return 操作状态
 * @retval 0 成功
 * @retval -1 失败
 */
int wifi_setsockopt (int s, int level, int optname, const void *optval, socklen_t optlen)
{
	//TODO 未实现
	return -1;
}
